﻿Imports System.ServiceModel
Imports System.ServiceModel.Configuration
Imports System.Security.Cryptography
Imports System.IO
Imports System.Net
Imports System.Configuration

Public Class MysteryEncryptedClient

    Private _server_disp As IMysteryEncryptedService
    Private _dispatcher As New MysteryDispacther
    Private pipeFactoryDisp As ChannelFactory(Of IMysteryEncryptedService)
    Private pipeProxyDisp As IClientChannel

    Private Shared _server_rsa As RSACryptoServiceProvider

    Private _endpoint_name As String
    Private _handler As IMysteryEncryptedServiceEventHandler

    Public Property client_name As String
    Public Property token As String
    Public Property user As User

    Private _ready As Boolean = False
    Public ReadOnly Property ready As Boolean
        Get
            Return _ready
        End Get
    End Property

    Public Sub New(endpoint_name As String, handler As IMysteryEncryptedServiceEventHandler)
        _endpoint_name = endpoint_name
        _handler = handler
        If _handler Is Nothing Then _handler = New DefaultEncryptedServiceEventHandler
        resetServer()
    End Sub

    Sub resetServer()
        _dispatcher.invoke(Sub()
                               Dim self_ref As EventHandler = AddressOf Me.resetServer
                               If pipeProxyDisp IsNot Nothing Then
                                   RemoveHandler pipeProxyDisp.Faulted, self_ref
                                   RemoveHandler pipeProxyDisp.Closed, self_ref
                               End If

                               Dim group As ServiceModelSectionGroup = ServiceModelSectionGroup.GetSectionGroup(ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None))
                               If group IsNot Nothing Then

                                   pipeFactoryDisp = New ChannelFactory(Of IMysteryEncryptedService)(_endpoint_name)
                                   'create a communication channel and register for its events
                                   pipeProxyDisp = pipeFactoryDisp.CreateChannel()
                                   Try
                                       pipeProxyDisp.Open()
                                       AddHandler pipeProxyDisp.Faulted, self_ref
                                       AddHandler pipeProxyDisp.Closed, self_ref

                                       _server_disp = pipeProxyDisp

                                       Dim rsa As New RSACryptoServiceProvider
                                       rsa.PersistKeyInCsp = False
                                       Dim PublicXmlKey As String = _server_disp.getPublicKey().XmlString

                                       Dim conf As MysteryConfiguration = getGlobalObject(Of MysteryConfiguration)()
                                       Dim server_key_path As String = System.IO.Path.Combine(conf.files_root_folder.FullName, _endpoint_name & ".xml")
                                       Dim FileXmlKey As String = String.Empty

                                       If System.IO.File.Exists(server_key_path) Then
                                           Dim reader As New System.IO.StreamReader(server_key_path)
                                           FileXmlKey = reader.ReadToEnd()
                                           reader.Close()
                                       End If

                                       If String.IsNullOrEmpty(FileXmlKey) Then
                                           If _handler.serverPublicKeyNotFound() Then
                                               Dim writer As New System.IO.StreamWriter(server_key_path)
                                               writer.Write(PublicXmlKey)
                                               writer.Close()
                                           Else
                                               Throw New Exception("cancelled")
                                           End If

                                       ElseIf PublicXmlKey <> FileXmlKey Then
                                           If _handler.serverPublicKeyChanged() Then
                                               Dim writer As New System.IO.StreamWriter(server_key_path)
                                               writer.Write(PublicXmlKey)
                                               writer.Close()
                                           Else
                                               Throw New Exception("cancelled")
                                           End If
                                       End If

                                       rsa.FromXmlString(PublicXmlKey)
                                       _server_rsa = rsa

                                       _ready = True
                                   Catch ex As Exception
                                       Throw New Exception("sorry can't open " & GetType(IMysteryEncryptedService).Name & " " & _endpoint_name)
                                   End Try

                               End If
                           End Sub)
    End Sub


    Public Function EcriptSendAndDescriptAnswer(message As Object) As Object
        Dim answer As WebMessage = Nothing
        Dim input As Object = WebMessage.GenerateMessage(client_name, message, _server_rsa, token)
        _dispatcher.invoke(Sub()
                               answer = _server_disp.message(input)
                           End Sub)
        If answer Is Nothing Then Return Nothing
        Return WebMessage.readMessage(answer, _server_rsa)
    End Function


    Public Function autheticate(username As String, password As String, Optional ByRef message As String = "") As Boolean
        Dim net_result As AuthenticateOutput = Nothing
        _dispatcher.invoke(Sub()
                               net_result = _server_disp.Authenticate(New AuthenticateInput With {.username = username,
                                                                                                  .PublicKeyXml = getGlobalObject(Of IMyRsaProvider)().getMySra.ToXmlString(False),
                                                                                                  .password = _server_rsa.Encrypt(password.getBytes, False)})
                           End Sub)
        message = net_result.message
        If net_result IsNot Nothing AndAlso net_result.success Then
            client_name = username
            token = net_result.token.readAEncryptedString
            Return True
        Else
            Return False
        End If

    End Function

End Class
